package listen;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import users.GroupTask;
import users.TaskData;
import users.UserAccount;

import logging.DateStamp;
import logging.LogWriter;

// Multi-threaded server. Receives request and sends "200 response to client.
public class Listen {
	private ServerSocket server;
	private Socket client = null;
	private static int threadID = 0;
	private ExecutorService threadPoolExecutor;
	
	private final int portNumber = 9226;
	private final int maxThreads = 10;
	private final LogWriter serverLog = new LogWriter();

	private AccountDatabase db = new AccountDatabase();
	
	public void webServerRun() throws InterruptedException {
		// Open server socket.
		try {
			server = new ServerSocket(portNumber);
			serverLog.log("Listening to port " + portNumber + ".");
		} catch (IOException ie) {
			System.out.println("Failed to open socket on port " + portNumber + ": " + ie);
			System.exit(-1);
		}

		// Set up thread pool.
		threadPoolExecutor = Executors.newFixedThreadPool(maxThreads);

		// Listen for client.
		while (true) {
			try {
				client = server.accept();
				threadPoolExecutor.execute(new RequestThread(client, threadID, db));

				// Cycles through thread pool identifiers.
				if (threadID == maxThreads) {
					threadID = 0;
				} else {
					threadID++;
				}
			} catch (IOException ie) {
				serverLog.setThreadID(threadID);
				serverLog.log("failed with client (" + client.getInetAddress());
				System.exit(-1);
			}
		}
	}

	public static void main(String[] args) throws IOException, InterruptedException {
		Listen listenServer = new Listen();
		listenServer.webServerRun();
	}
}

class RequestThread implements Runnable {
	private Socket client;
	private BufferedInputStreamReader requestStream;
	private BufferedOutputStream out;
	private int threadID;
	private AccountDatabase db;

	private LogWriter serverLog = new LogWriter();

	public RequestThread(Socket clientSocket, int tID, AccountDatabase db) {
		this.client = clientSocket;
		this.threadID = tID;
		this.db = db;

		serverLog.setThreadID(tID);

		try {
			requestStream = new BufferedInputStreamReader(new BufferedInputStream(client.getInputStream()));
			out = new BufferedOutputStream(client.getOutputStream());
		} catch (IOException ie) {
			serverLog.log("Error in Request Thread: " + ie);
		}
	}

	// Process client request.
	public void run() {
		try {
			BufferedReader fromClient = new BufferedReader(new InputStreamReader(client.getInputStream()));

			// Reads the first line to get the request method, URI and HTTP version
			String line = fromClient.readLine();
			for(int x = 0; x < 8; x++) {
				serverLog.log(line);
				line = fromClient.readLine();
			}

			serverLog.log("Receiving from Client (" + client.getInetAddress() + ")");
			serverLog.log("Client sent: " + line);
			
			sendResponse(new ServerAction(line, db).Action());
		} catch (IOException ie) {
			serverLog.log("IO failed: " + ie);
			System.exit(-1);
		}
	}

	// Send back to client any data. Currently on sends "200" message.
	private void sendResponse(String sendText) {
		DateStamp date = new DateStamp();

		try {
			BufferedOutputStream outputToClient = new BufferedOutputStream(client.getOutputStream());
			PrintWriter writer = new PrintWriter(outputToClient, true);
			serverLog.setWriter(writer);

			serverLog.log("Sending to Client ");

			serverLog.sendToClient("HTTP/1.1 200 OK");
			serverLog.sendToClient("Server: LISTEN");
			serverLog.sendToClient("Date: "	+ date.printDate("EEE, d MMM yyyy HH:mm:ss z"));
			serverLog.sendToClient("Content-Type: text/plain");
			serverLog.sendToClient("Connection: close");
			serverLog.sendToClient("Content-Length: " + sendText.length());
			serverLog.sendToClient("Age: 0");

			writer.println("");
			//writer.println(sendText + "\n\r");
			serverLog.sendToClient(sendText + "\n\r");

			writer.flush();
			writer.close();

			serverLog.log("Closed connection with " + client.getInetAddress());

			out.close();
			client.close();
		} catch (IOException ex) {
			serverLog.log("Access Failed (" + client.getInetAddress() + ": " + ex.getMessage());
		}
	}
}